package com.letoo.sso.config;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import com.github.miemiedev.mybatis.paginator.OffsetLimitInterceptor;
import com.letoo.sso.dataaccess.datasource.DynamicDataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
@EnableTransactionManagement
public class MyBatisConfig implements TransactionManagementConfigurer {
  @Value("${jdbc.db.list}")
  private String dbList;
  // db common#
  @Value("${jdbc.driverClassName.common}")
  private String driverClassName;
  // master
  @Value("${jdbc.master.url}")
  private String masterUrl;
  @Value("${jdbc.master.username}")
  private String masterUsername;
  @Value("${jdbc.master.password}")
  private String masterPassword;
  @Value("${jdbc.master.maximumPoolSize}")
  private Integer masterMaximumPoolSize;
  @Value("${jdbc.master.minimumIdle}")
  private Integer masterMinimumIdle;
  @Value("${jdbc.master.idleTimeout}")
  private Long masterIdleTimeout;
  @Value("${jdbc.master.connectionTestQuery}")
  private String masterConnectionTestQuery;
  // dataSourceProperties
  @Value("${jdbc.master.cachePrepStmts}")
  private String masterCachePrepStmts;
  @Value("${jdbc.master.prepStmtCacheSize}")
  private String masterPrepStmtCacheSize;
  @Value("${jdbc.master.prepStmtCacheSqlLimit}")
  private String masterPrepStmtCacheSqlLimit;
  @Value("${jdbc.master.useServerPrepStmts}")
  private String masterUseServerPrepStmts;
  // master

// slaveone
  @Value("${jdbc.slaveone.url}")
  private String slaveoneUrl;
  @Value("${jdbc.slaveone.username}")
  private String slaveoneUsername;
  @Value("${jdbc.slaveone.password}")
  private String slaveonePassword;
  @Value("${jdbc.slaveone.maximumPoolSize}")
  private Integer slaveoneMaximumPoolSize;
  @Value("${jdbc.slaveone.minimumIdle}")
  private Integer slaveoneMinimumIdle;
  @Value("${jdbc.slaveone.idleTimeout}")
  private Long slaveoneIdleTimeout;
  @Value("${jdbc.slaveone.connectionTestQuery}")
  private String slaveoneConnectionTestQuery;
  // dataSourceProperties
  @Value("${jdbc.slaveone.cachePrepStmts}")
  private String slaveoneCachePrepStmts;
  @Value("${jdbc.slaveone.prepStmtCacheSize}")
  private String slaveonePrepStmtCacheSize;
  @Value("${jdbc.slaveone.prepStmtCacheSqlLimit}")
  private String slaveonePrepStmtCacheSqlLimit;
  @Value("${jdbc.slaveone.useServerPrepStmts}")
  private String slaveoneUseServerPrepStmts;
  // slaveone

// slavetwo
  @Value("${jdbc.slavetwo.url}")
  private String slavetwoUrl;
  @Value("${jdbc.slavetwo.username}")
  private String slavetwoUsername;
  @Value("${jdbc.slavetwo.password}")
  private String slavetwoPassword;
  @Value("${jdbc.slavetwo.maximumPoolSize}")
  private Integer slavetwoMaximumPoolSize;
  @Value("${jdbc.slavetwo.minimumIdle}")
  private Integer slavetwoMinimumIdle;
  @Value("${jdbc.slavetwo.idleTimeout}")
  private Long slavetwoIdleTimeout;
  @Value("${jdbc.slavetwo.connectionTestQuery}")
  private String slavetwoConnectionTestQuery;
// dataSourceProperties
  @Value("${jdbc.slavetwo.cachePrepStmts}")
  private String slavetwoCachePrepStmts;
  @Value("${jdbc.slavetwo.prepStmtCacheSize}")
  private String slavetwoPrepStmtCacheSize;
  @Value("${jdbc.slavetwo.prepStmtCacheSqlLimit}")
  private String slavetwoPrepStmtCacheSqlLimit;
  @Value("${jdbc.slavetwo.useServerPrepStmts}")
  private String slavetwoUseServerPrepStmts;
// slavetwo

  // @Bean注解在方法上,返回值是一个类的实例,并声明这个返回值(返回一个对象)是spring上下文环境中的一个bean
  @Bean(name = "dataSourceMaster", destroyMethod = "close")
  @Primary
  public HikariDataSource getDataSourceMaster() {
    HikariConfig configuration = new HikariConfig();
    // 设置属性
    configuration.setDriverClassName(this.driverClassName);
    configuration.setJdbcUrl(masterUrl);
    configuration.setUsername(masterUsername);
    configuration.setPassword(masterPassword);
    configuration.setMaximumPoolSize(masterMaximumPoolSize);
    configuration.setMinimumIdle(masterMinimumIdle);
    configuration.setIdleTimeout(masterIdleTimeout);
    configuration.setConnectionTestQuery(masterConnectionTestQuery);
    Properties dsProperties = new Properties();
    dsProperties.setProperty("cachePrepStmts", masterCachePrepStmts);
    dsProperties.setProperty("prepStmtCacheSize", masterPrepStmtCacheSize);
    dsProperties.setProperty("prepStmtCacheSqlLimit", masterPrepStmtCacheSqlLimit);
    dsProperties.setProperty("useServerPrepStmts", masterUseServerPrepStmts);
    configuration.setDataSourceProperties(dsProperties);
    return new HikariDataSource(configuration);
  }

  @Bean(name = "dataSourceSlaveOne", destroyMethod = "close")
  @Primary
  public HikariDataSource getDataSourceSlaveOne() {
    HikariConfig configuration = new HikariConfig();
    // 设置属性
    configuration.setDriverClassName(this.driverClassName);
    configuration.setJdbcUrl(slaveoneUrl);
    configuration.setUsername(slaveoneUsername);
    configuration.setPassword(slaveonePassword);
    configuration.setMaximumPoolSize(slaveoneMaximumPoolSize);
    configuration.setMinimumIdle(slaveoneMinimumIdle);
    configuration.setIdleTimeout(slaveoneIdleTimeout);
    configuration.setConnectionTestQuery(slaveoneConnectionTestQuery);
    Properties dsProperties = new Properties();
    dsProperties.setProperty("cachePrepStmts", slaveoneCachePrepStmts);
    dsProperties.setProperty("prepStmtCacheSize", slaveonePrepStmtCacheSize);
    dsProperties.setProperty("prepStmtCacheSqlLimit", slaveonePrepStmtCacheSqlLimit);
    dsProperties.setProperty("useServerPrepStmts", slaveoneUseServerPrepStmts);
    configuration.setDataSourceProperties(dsProperties);
    return new HikariDataSource(configuration);
  }

  @Bean(name = "dataSourceSlaveTwo", destroyMethod = "close")
  @Primary
  public HikariDataSource getDataSourceSlaveTwo() {
    HikariConfig configuration = new HikariConfig();
    // 设置属性
    configuration.setDriverClassName(this.driverClassName);
    configuration.setJdbcUrl(slavetwoUrl);
    configuration.setUsername(slavetwoUsername);
    configuration.setPassword(slavetwoPassword);
    configuration.setMaximumPoolSize(slavetwoMaximumPoolSize);
    configuration.setMinimumIdle(slavetwoMinimumIdle);
    configuration.setIdleTimeout(slavetwoIdleTimeout);
    configuration.setConnectionTestQuery(slavetwoConnectionTestQuery);
    Properties dsProperties = new Properties();
    dsProperties.setProperty("cachePrepStmts", slavetwoCachePrepStmts);
    dsProperties.setProperty("prepStmtCacheSize", slavetwoPrepStmtCacheSize);
    dsProperties.setProperty("prepStmtCacheSqlLimit", slavetwoPrepStmtCacheSqlLimit);
    dsProperties.setProperty("useServerPrepStmts", slavetwoUseServerPrepStmts);
    configuration.setDataSourceProperties(dsProperties);
    return new HikariDataSource(configuration);
  }

  @Bean(name = "dataSource")
  @Primary
  public DynamicDataSource getDataSource() {
    DynamicDataSource dynamicDataSource = new DynamicDataSource();
    // 设置属性
    Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
    targetDataSources.put("master", getDataSourceMaster());
    targetDataSources.put("slaveOne", getDataSourceSlaveOne());
    targetDataSources.put("slaveTwo", getDataSourceSlaveTwo());
    dynamicDataSource.setTargetDataSources(targetDataSources);
    dynamicDataSource.setDefaultTargetDataSource(getDataSourceMaster());
    return dynamicDataSource;
  }

  @Bean(name = "sqlSessionFactory")
  @Primary
  public SqlSessionFactory getSqlSessionFactory() throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    // 设置属性
    sqlSessionFactoryBean.setTypeAliasesPackage("com.letoo.dragon.dataaccess.domain");
    sqlSessionFactoryBean.setDataSource(getDataSource());
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    sqlSessionFactoryBean.setConfigLocation(resolver.getResource("classpath:/sqlmap-config.xml"));
    ResourcePatternResolver resolverTwo = new PathMatchingResourcePatternResolver();
    sqlSessionFactoryBean.setMapperLocations(resolverTwo.getResources("classpath*:/sqlmap/*.xml"));
    // 设置分页plugin mybatis.paginator
    OffsetLimitInterceptor offsetLimitInterceptor = new OffsetLimitInterceptor();
    // 添加分页插件mybatis.paginator，支持mysql方言如果需要多类型DB需要在申明新的sqlSessionFactory
    offsetLimitInterceptor.setDialectClass("com.github.miemiedev.mybatis.paginator.dialect.MySQLDialect");
    Interceptor[] interceptors = new Interceptor[] {offsetLimitInterceptor};
    sqlSessionFactoryBean.setPlugins(interceptors);
    // 设置分页plugin mybatis.paginator
    return sqlSessionFactoryBean.getObject();
  }

  @Bean(name = "sqlSessionTemplate")
  @Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
  public SqlSessionTemplate getSqlSessionTemplate() throws Exception {
    SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(getSqlSessionFactory());
    // 设置属性
    return sqlSessionTemplate;
  }

  @Bean(name = "transactionManager")
  public DataSourceTransactionManager getTransactionManager() {
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(getDataSource());
    // 设置属性
    return dataSourceTransactionManager;
  }

  @Override
  public PlatformTransactionManager annotationDrivenTransactionManager() {
    return getTransactionManager();
  }

}
